package com.flydog.taskManager.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.flydog.taskManager.common.api.PageResponse;
import com.flydog.taskManager.common.converter.ProcessStatusConverter;
import com.flydog.taskManager.dto.*;
import com.flydog.taskManager.entity.PhasedTask;
import com.flydog.taskManager.entity.Task;
import com.flydog.taskManager.entity.TaskBrief;
import com.flydog.taskManager.repository.PhasedTaskRepository;
import com.flydog.taskManager.repository.TaskBriefRepository;
import com.flydog.taskManager.security.config.SecurityUtil;
import com.flydog.taskManager.service.PhasedTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

import static com.flydog.taskManager.repository.PhasedTaskSpecs.searchSpecs;


/**
 * @author Cao
 * @description 阶段性工作服务实现类
 * @create 2021-05-10
 */
@Slf4j
@Service
public class PhasedTaskServiceImpl implements PhasedTaskService {

    @Autowired
    PhasedTaskRepository phasedTaskRepository;

    @Autowired
    TaskBriefRepository taskBriefRepository;

    @Autowired
    SecurityUtil securityUtil;

    @Autowired
    ProcessStatusConverter statusConverter;

    @Override
    public PhasedTask createTask(PhasedTaskParam param) {
        PhasedTask phasedTask = new PhasedTask();
        BeanUtils.copyProperties(param, phasedTask);
        phasedTask.setClassify(0);
        phasedTask.setProcessStatus(ProcessStatus.PROCESSING);
        if (ObjectUtil.isNull(param.getLevel())) {
            phasedTask.setLevel(0);
        }
        if (ObjectUtil.isNull(param.getParentId())) {
            phasedTask.setParentId(0L);
        } else {
            phasedTask.setParentId(param.getParentId());
            Optional<PhasedTask> parent = phasedTaskRepository.findById(param.getParentId());
            parent.ifPresent(task -> phasedTask.setLevel(task.getLevel() + 1));
        }
        phasedTask.setUserId(securityUtil.getAuthedUser().getId());
        return phasedTaskRepository.save(phasedTask);
    }

    @Override
    public PhasedTask update(Long id, PhasedTaskParam param) {
        Optional<PhasedTask> taskOptional = phasedTaskRepository.findById(id);
        if (taskOptional.isPresent()) {
            PhasedTask task = taskOptional.get();
            BeanUtils.copyProperties(param, task);
            return phasedTaskRepository.save(task);
        }
        return null;
    }


    @Override
    @Transactional
    public Set<Long> deleteTask(Long[] ids) {
        Set<Long> result = new HashSet<>();
        List<PhasedTask> rootTasks = phasedTaskRepository.findAllById(Arrays.asList(ids));
        for (PhasedTask rootTask : rootTasks) {
            result.add(rootTask.getId());
            List<TaskBrief> briefs = taskBriefRepository.findByPhaseTaskIdOrderByCreateTimeDesc(rootTask.getId());
            taskBriefRepository.deleteAll(briefs);
            List<PhasedTask> childTasks = phasedTaskRepository.findByParentId(rootTask.getId());
            Long[] childTaskIds = childTasks.stream().map(Task::getId).toArray(Long[]::new);
            result.addAll(deleteTask(childTaskIds));
        }
        phasedTaskRepository.deleteAll(rootTasks);
        return result;
    }

    @Override
    public Optional<PhasedTask> getTask(Long id) {
        return phasedTaskRepository.findById(id);
    }

    @Override
    public List<PhasedTask> listByEndDate(Calendar begin, Calendar end) {
        Long userId = securityUtil.getAuthUserId();
        return phasedTaskRepository.findByEndBetweenAndUserId(begin, end, userId);
    }

    @Override
    public PageResponse<PhasedTask> listByBeginDate(Calendar begin, Calendar end, int pageNum, int pageSize) {
        Long userId = securityUtil.getAuthUserId();
        PageRequest request = PageRequest.of(pageNum - 1, pageSize, Sort.Direction.ASC, "begin");
        Page<PhasedTask> pageRequest = phasedTaskRepository.findByBeginBetweenAndUserId(begin, end, userId, request);
        return PageResponse.restPage(pageRequest);
    }

    @Override
    public List<PhasedTask> listByBeginDate(Calendar begin, Calendar end) {
        Long userId = securityUtil.getAuthUserId();
        return phasedTaskRepository.findByBeginBetweenAndUserId(begin, end, userId);
    }

    @Override
    public List<PhasedTaskWithChildren> listTaskWithChildren(PhaseTaskSearchParam searchParam) {
        List<PhasedTaskWithChildren> result = new ArrayList<>();
        Long userId = securityUtil.getAuthedUser().getId();
        if (searchParam.isParamNull()) {
            List<PhasedTask> rootTasks = phasedTaskRepository.findByLevelAndUserIdOrderByBeginDesc(0, userId);
            for (PhasedTask task : rootTasks) {
                PhasedTaskWithChildren rootItem = new PhasedTaskWithChildren();
                BeanUtils.copyProperties(task, rootItem);
                rootItem.setChildren(listTaskWithChildrenById(rootItem.getId()));
                result.add(rootItem);
            }
        } else {
            List<PhasedTask> tasks = phasedTaskRepository.findAll(searchSpecs(searchParam, userId));
            result = tasks.stream().map(task -> {
                PhasedTaskWithChildren task1 = new PhasedTaskWithChildren();
                BeanUtils.copyProperties(task, task1);
                return task1;
            }).collect(Collectors.toList());
        }
        return result;
    }

    @Override
    public PhasedTask updateProcessStatus(Long id, int status) {
        Optional<PhasedTask> task = phasedTaskRepository.findById(id);
        if (task.isPresent()) {
            PhasedTask taskEntity = task.get();
            taskEntity.setProcessStatus(statusConverter.convertToEntityAttribute(status));
            TaskBrief taskBrief = new TaskBrief();
            taskBrief.setPhaseTaskId(taskEntity.getId());
            taskBrief.setSubject("完成工作");
            taskBrief.setBrief("用户设置工作完成");
            taskBrief.setCreateTime(Calendar.getInstance().getTime());
            taskBriefRepository.save(taskBrief);
            return phasedTaskRepository.save(taskEntity);
        }
        return null;
    }

    @Override
    public PhasedTaskTreeStructure getTree(Long id) {
        Long rootId = findRootTaskId(id);
        Optional<PhasedTask> taskOptional = phasedTaskRepository.findById(rootId);
        if (taskOptional.isPresent()) {
            PhasedTask rootTask = taskOptional.get();
            PhasedTaskWithChildren rootItem = new PhasedTaskWithChildren();
            BeanUtils.copyProperties(rootTask, rootItem);
            rootItem.setChildren(listTaskWithChildrenById(rootItem.getId()));
            return rootItem.paresToTreeStructure(id);
        }
        return null;
    }

    @Override
    public List<PhasedTask> setStatusBeforeData(int processStatus, Calendar date) {
        List<PhasedTask> preChangeTasks =
                phasedTaskRepository.findByEndIsLessThanEqualAndProcessStatusEquals(date, ProcessStatus.PROCESSING);
        List<PhasedTask> result = new ArrayList<>();
        for (PhasedTask task : preChangeTasks) {
            task.setProcessStatus(statusConverter.convertToEntityAttribute(processStatus));
            result.add(phasedTaskRepository.save(task));
        }
        return result;
    }

    @Override
    public PageResponse<PhasedTask> listByEndDate(Calendar begin, Calendar end, int pageNum, int pageSize) {
        Long userId = securityUtil.getAuthUserId();
        PageRequest request = PageRequest.of(pageNum - 1, pageSize, Sort.Direction.ASC, "end");
        Page<PhasedTask> pageRequest = phasedTaskRepository.findByEndBetweenAndUserId(begin, end, userId,request);
        return PageResponse.restPage(pageRequest);
    }

    private List<PhasedTaskWithChildren> listTaskWithChildrenById(Long id) {
        List<PhasedTaskWithChildren> result = new ArrayList<>();
        List<PhasedTask> children = phasedTaskRepository.findByParentId(id);
        for (PhasedTask child : children) {
            PhasedTaskWithChildren childItem = new PhasedTaskWithChildren();
            BeanUtils.copyProperties(child, childItem);
            childItem.setChildren(listTaskWithChildrenById(child.getId()));
            result.add(childItem);
        }
        return result;
    }

    private Long findRootTaskId(Long taskId) {
        Optional<PhasedTask> current = phasedTaskRepository.findById(taskId);
        while (current.isPresent() && current.get().getParentId() != 0) {
            current = phasedTaskRepository.findById(current.get().getParentId());
        }
        return current.get().getId();
    }


}
